1   /*
2    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 6398884
27   * @summary Test that a method inherited from two different interfaces
28   *          appears only once in MBeanInfo.
29   * @author dfuchs
30   * @run clean TooManyFooTest
31   * @run build TooManyFooTest
32   * @run main TooManyFooTest
33   */
34  
35  import java.lang.management.ManagementFactory;
36  import java.lang.reflect.Method;
37  import java.util.Arrays;
38  import java.util.HashMap;
39  import java.util.HashSet;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.logging.Logger;
43  import javax.management.Descriptor;
44  import javax.management.MBeanInfo;
45  import javax.management.MBeanOperationInfo;
46  import javax.management.MBeanServer;
47  import javax.management.ObjectName;
48  import javax.management.StandardMBean;
49  import javax.management.openmbean.OpenMBeanOperationInfo;
50  
51  /**
52   * Class TooManyFooTest
53   * @author Sun Microsystems, 2005 - All rights reserved.
54   */
55  public class TooManyFooTest {
56  
57      /**
58       * A logger for this class.
59       **/
60      private static final Logger LOG =
61              Logger.getLogger(TooManyFooTest.class.getName());
62  
63      public static class NumberHolder {
64          public Integer getNumber() { return 0;}
65          public void setNumber(Integer n) {};
66      }
67      public static class MyNumberHolder extends NumberHolder {
68  
69      }
70      public interface Parent1 {
71          public int foo(); // Both in Parent1 and Parent2
72          public Integer barfoo(); // Subtype in Parent1, Super type in Parent2
73          public Long    foobar(); // Subtype in Parent1 & MBean, Super type in
74                                   // Parent2
75          public Number  toofoo(); // Subtype in Parent1, Super type in Parent2
76                                   // Concrete type in MBean
77          public Object toofoofoo(); // Super type in Parent1, Subtype in Parent2,
78          public NumberHolder toobarbar(); // toofoofoo reversed
79      }
80  
81      public interface Parent2 {
82          public int foo(); // Both in Parent1 and Parent2
83          public Number barfoo();
84          public Number foobar();
85          public Object toofoo();
86          public NumberHolder  toofoofoo();
87          public Object toobarbar();
88      }
89  
90      public interface ChildMBean extends Parent1, Parent2 {
91          public Long foobar();
92          public Long toofoo();
93      }
94  
95      public interface ChildMXBean extends Parent1, Parent2 {
96          public Long foobar();
97          public Long toofoo();
98      }
99  
100     public interface ChildMixMXBean extends ChildMBean, ChildMXBean {
101     }
102 
103     public static class Child implements ChildMBean {
104         public int foo() {return 0;}
105         public Long foobar() {return 0L;}
106         public Long toofoo() {return 0L;}
107         public Integer barfoo() {return 0;}
108         public MyNumberHolder toofoofoo() { return null;}
109         public MyNumberHolder toobarbar() { return null;}
110     }
111 
112     public static class ChildMix implements ChildMXBean {
113         public int foo() {return 0;}
114         public Long foobar() {return 0L;}
115         public Long toofoo() {return 0L;}
116         public Integer barfoo() {return 0;}
117         public MyNumberHolder toofoofoo() { return null;}
118         public MyNumberHolder toobarbar() { return null;}
119     }
120 
121     public static class ChildMixMix extends Child implements ChildMixMXBean {
122     }
123 
124 
125     /** Creates a new instance of TooManyFooTest */
126     public TooManyFooTest() {
127     }
128 
129     private static final int OPCOUNT;
130     private static final Map<String,String> EXPECTED_TYPES;
131     private static final String[][] type_array = {
132         { "foo", int.class.getName() },
133         { "foobar", Long.class.getName()},
134         { "toofoo", Long.class.getName()},
135         { "barfoo", Integer.class.getName()},
136         { "toofoofoo", NumberHolder.class.getName()},
137         { "toobarbar", NumberHolder.class.getName()},
138     };
139     static {
140         try {
141             final Set<String> declared = new HashSet<String>();
142             for (Method m:Child.class.getDeclaredMethods()) {
143                 declared.add(m.getName()+Arrays.asList(m.getParameterTypes()));
144             }
145             final Set<String> exposed = new HashSet<String>();
146             for (Method m:ChildMBean.class.getMethods()) {
147                 exposed.add(m.getName()+Arrays.asList(m.getParameterTypes()));
148             }
149             declared.retainAll(exposed);
150             OPCOUNT = declared.size();
151             EXPECTED_TYPES = new HashMap<String,String>();
152             for (String[] st:type_array) {
153                 EXPECTED_TYPES.put(st[0],st[1]);
154             }
155         } catch (Exception x) {
156             throw new ExceptionInInitializerError(x);
157         }
158     }
159 
160     private static void test(Object child, String name, boolean mxbean)
161         throws Exception {
162         final ObjectName childName =
163                 new ObjectName("test:type=Child,name="+name);
164         final MBeanServer server =
165                 ManagementFactory.getPlatformMBeanServer();
166         server.registerMBean(child,childName);
167         try {
168             final MBeanInfo info = server.getMBeanInfo(childName);
169             System.out.println(name+": " + info.getDescriptor());
170             final int len = info.getOperations().length;
171             if (len == OPCOUNT) {
172                 System.out.println(name+": OK, only "+OPCOUNT+
173                         " operations here...");
174             } else {
175                 final String qual = (len>OPCOUNT)?"many":"few";
176                 System.err.println(name+": Too "+qual+" foos! Found "+
177                         len+", expected "+OPCOUNT);
178                 for (MBeanOperationInfo op : info.getOperations()) {
179                     System.err.println("public "+op.getReturnType()+" "+
180                             op.getName()+"();");
181                 }
182                 throw new RuntimeException("Too " + qual +
183                         " foos for "+name);
184             }
185 
186             final Descriptor d = info.getDescriptor();
187             final String mxstr = String.valueOf(d.getFieldValue("mxbean"));
188             final boolean mxb =
189                     (mxstr==null)?false:Boolean.valueOf(mxstr).booleanValue();
190             System.out.println(name+": mxbean="+mxb);
191             if (mxbean && !mxb)
192                 throw new AssertionError("MXBean is not OpenMBean?");
193 
194             for (MBeanOperationInfo mboi : info.getOperations()) {
195 
196                 // Sanity check
197                 if (mxbean && !mboi.getName().equals("foo")) {
198                     // The spec doesn't guarantee that the MBeanOperationInfo
199                     // of an MXBean will be an OpenMBeanOperationInfo, and in
200                     // some circumstances in our implementation it will not.
201                     // However, in thsi tests, for all methods but foo(),
202                     // it should.
203                     //
204                     if (!(mboi instanceof OpenMBeanOperationInfo))
205                         throw new AssertionError("Operation "+mboi.getName()+
206                                 "() is not Open?");
207                 }
208 
209                 final String exp = EXPECTED_TYPES.get(mboi.getName());
210 
211                 // For MXBeans, we need to compare 'exp' with the original
212                 // type - because mboi.getReturnType() returns the OpenType
213                 //
214                 String type = (String)mboi.getDescriptor().
215                             getFieldValue("originalType");
216                 if (type == null) type = mboi.getReturnType();
217                 if (type.equals(exp)) continue;
218                 System.err.println("Bad return type for "+
219                         mboi.getName()+"! Found "+type+
220                         ", expected "+exp);
221                 throw new RuntimeException("Bad return type for "+
222                         mboi.getName());
223             }
224         } finally {
225             server.unregisterMBean(childName);
226         }
227     }
228 
229     public static void main(String[] args) throws Exception {
230         final Child child = new Child();
231         test(child,"Child[MBean]",false);
232         final ChildMix childx = new ChildMix();
233         test(childx,"ChildMix[MXBean]",true);
234         final ChildMixMix childmx = new ChildMixMix();
235         test(childmx,"ChildMixMix[MXBean]",false);
236         final StandardMBean schild = new StandardMBean(child,ChildMBean.class);
237         test(schild,"Child[StandarMBean(Child)]",false);
238         final StandardMBean schildx =
239                 new StandardMBean(childx,ChildMXBean.class,true);
240         test(schildx,"ChildMix[StandarMXBean(ChildMix)]",true);
241         final StandardMBean schildmx =
242                 new StandardMBean(childmx,ChildMixMXBean.class,true);
243         test(schildmx,"ChildMixMix[StandarMXBean(ChildMixMix)]",true);
244     }
245 
246 }